package com.zengdw.logRecord.aop;

import com.zengdw.logRecord.config.LogRecord;
import com.zengdw.logRecord.domain.LogOperations;
import com.zengdw.logRecord.domain.MethodExecuteResult;
import com.zengdw.logRecord.resolve.LogRecordContext;
import com.zengdw.logRecord.resolve.LogRecordOperationSource;
import com.zengdw.logRecord.resolve.LogRecordValueParser;
import com.zengdw.logRecord.resolve.ParseFunctionFactory;
import com.zengdw.logRecord.service.ILogRecordService;
import com.zengdw.logRecord.service.IOperatorService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.expression.EvaluationContext;

import java.lang.reflect.Method;

/**
 * 日志切面
 *
 * @author zengd
 * @date 2021/9/18 10:48
 */
@Aspect
@Slf4j
public class LogRecordAspect {

    private final LogRecordOperationSource logRecordOperationSource;
    private final LogRecordValueParser logRecordValueParser;
    private final ILogRecordService logRecordService;

    public LogRecordAspect(IOperatorService operatorService, ParseFunctionFactory parseFunctionFactory,
                           ILogRecordService logRecordService) {
        this.logRecordValueParser = new LogRecordValueParser(parseFunctionFactory);
        this.logRecordOperationSource = new LogRecordOperationSource(operatorService);
        this.logRecordService = logRecordService;
    }

    @Pointcut("@annotation(com.zengdw.logRecord.config.LogRecord)")
    public void point() {

    }

    @Around(value = "point()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Class<?> targetClass = pjp.getTarget().getClass();
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        Object[] args = pjp.getArgs();
        Object o = null;
        LogRecord logRecord = method.getAnnotation(LogRecord.class);
        MethodExecuteResult result = new MethodExecuteResult(true, null, "");
        LogRecordContext.putEmptySpan();
        LogOperations operations = new LogOperations();

        try {
            operations = logRecordOperationSource.computeLogRecordOperations(logRecord);
            resolveExpression(targetClass, method, args, "", operations, "", true);
        } catch (Exception e) {
            log.error("log record parse before function exception", e);
        }

        try {
            o = pjp.proceed();
        } catch (Throwable throwable) {
            result = new MethodExecuteResult(false, throwable, throwable.getMessage());
        }

        try {
            resolveExpression(targetClass, method, args, o, operations, result.getErrmsg(), false);
            logRecordService.addLog(operations);
        } catch (Exception e) {
            log.error("log record parse before function exception", e);
        } finally {
            LogRecordContext.clear();
        }

        if (result.getThrowable() != null) {
            throw result.getThrowable();
        }
        return o;
    }

    private void resolveExpression(Class<?> targetClass, Method method, Object[] args, Object o, LogOperations operations, String errmsg, boolean b) {
        EvaluationContext context = logRecordValueParser.createEvaluationContext(method, args, o, errmsg);
        logRecordValueParser.resolve(operations, targetClass, method, context, b);
    }

}
